/* 
 * $Id: dictc.c 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $ 
 *
 * Copyright (c) 2006, 2007, 2008 Prabhu Gurumurthy <pgurumur@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "util.h"
#include "cmdtab.h"

static EditLine *edit;
static History *hist;
static HistEvent hev;

int 
main(int argc, char *argv[])
{
#ifdef __OpenBSD__
   setproctitle("%d", getpid());
#endif
   signal(SIGTERM, handle_signal);
   signal(SIGHUP, handle_signal);
   signal(SIGINT, handle_signal);
   signal(SIGPIPE, handle_signal);

   if (argc <= 1) 
      usage();
   else {
      edit = el_init(__progname, stdin, stdout, stderr);
      if (!edit)
         error("%s el_init()", __func__);

      hist = history_init();
      if (!hist)
         error("%s history_init()", __func__);

      history(hist, &hev, H_SETSIZE, MAXHISTORY);
      history(hist, &hev, H_SETUNIQUE, 1);
      el_set(edit, EL_HIST, history, hist);
      el_set(edit, EL_EDITOR, DICT_EDITOR);
      el_set(edit, EL_PROMPT, get_prompt);
#if 0
      el_set(edit, EL_ADDFN, "dict-complete", 
            "Context sensitive argument completion",
            dict_complete);
      el_set(edit, EL_BIND, "^I", "dict-complete", NULL);
#endif
      el_source(edit, NULL);
      el_set(edit, EL_SIGNAL, 0);

      dclient = (struct dict_client *)malloc(sizeof(struct dict_client));
      if (!dclient)
         error("%s malloc()", __func__);

      dclient->conn= 0;
      dclient->serv_port = DICT_PORT;
      dclient->af = AF_INET;

      int ch = 0;
      while ((ch = getopt(argc, argv, "46p:")) != EOF) {
         switch (ch) {
            case '4':
               dclient->af = AF_INET;
               break;

            case '6':
               dclient->af = AF_INET6;
               break;

            case 'p':
               if (isdigit((int)*optarg))
                  dclient->serv_port = (int)strtol(optarg, (char **)NULL, 10);
               else
                  warning(
                        "%s: -p option requires numeric argument", __progname);
               break;

            default:
               usage();
               break;
         }
      }

      if ((argc - 1) != optind) 
         usage();

      dclient->serv_name = argv[optind];
   }


   create_socket();
   set_nonblock();

   if (dclient->af == AF_INET) {
      if (connect(
               dclient->sockfd, 
               (struct sockaddr *)&dclient->dsock4_info, 
               sizeof(dclient->dsock4_info)) != 0)
         if (errno != EINPROGRESS)
            error("%s connect()", __func__);
   }
   else if (dclient->af == AF_INET6) 
      if (connect(
               dclient->sockfd, 
               (struct sockaddr *)&dclient->dsock6_info, 
               sizeof(dclient->dsock6_info)) != 0)
         if (errno != EINPROGRESS)
            error("%s connect()", __func__);

   dclient->conn = 1;
   unset_nonblock();

   if (dclient->conn) {
      conn_quit = FALSE;
      sock_write(DICT_CLIENT_INFO);
      do_prompt();
   }

   cleanup();
   return 0;
}

void
usage(void)
{
   warning("usage: %s [-46] -p [port] server", __progname);
   exit(1);
}

char *
get_prompt()
{
   return DICT_PROMPT;
}

void 
create_socket()
{
   if (dclient->serv_name) {
      if (dclient->af == AF_INET) {
         struct hostent *host = gethostbyname2(dclient->serv_name, dclient->af);
         if (host) 
            memcpy(
                  &dclient->dsock4_info.sin_addr, 
                  host->h_addr_list[0], 
                  host->h_length);
         else
            error("%s gethostbyname2()", __func__);
         dclient->dsock4_info.sin_family = PF_INET;
         dclient->dsock4_info.sin_port = htons(dclient->serv_port);
      }
      else if (dclient->af == AF_INET6) {
         struct hostent *host = gethostbyname2(dclient->serv_name, dclient->af);
         if (host) 
            memcpy(
                  &dclient->dsock6_info.sin6_addr, 
                  host->h_addr_list[0], 
                  host->h_length);
         else
            error("%s gethostbyname2()", __func__);
         dclient->dsock6_info.sin6_family = PF_INET6;
         dclient->dsock6_info.sin6_port = htons(dclient->serv_port);
      }


      int protocol = 0;
      struct protoent *proto;
      if ((proto = getprotobyname("tcp")) != NULL)
         protocol = proto->p_proto;

      dclient->sockfd = socket(dclient->af, SOCK_STREAM, protocol);
      if (dclient->sockfd < 0)
         error("%s socket()", __func__);

      int set_opt = 0;
      const u_int32_t recvbuffer = CMDLINE_BUF;
      set_opt = setsockopt(
            dclient->sockfd, 
            SOL_SOCKET, 
            SO_RCVBUF, 
            &recvbuffer, 
            sizeof(u_int32_t));
      if (set_opt < 0) 
         error("setting recv buffer: %s setsockopt()", __func__);

   }
}

void 
do_prompt()
{
   boolean flag = TRUE;
   size_t length = 0;
   const char *buf;

   fputc('\n', stdout);
   printf("type 'help' any time to get help directly from the server\n");
   while (flag) {
      if (!dclient->conn)
         flag = FALSE;
      else {
         buf = el_gets(edit, (int *)&length);
         if (buf) {
			   size_t buf_len = strlen(buf);
			   if (buf_len != length)
				   length = buf_len;

            if (length > 1) {
               if (buf[--length] == '\n')
                  if (!length) 
                     warning("too few parameters in CLI");

               if (length >= MAX_CMDLINE)
                  warning("input line is too long");
               else {
                  memset(line, '\0', MAX_CMDLINE);
                  memcpy(line, buf, length);
                  parse_commands(line);
                  if (*buf != '!') 
                     history(hist, &hev, H_ENTER, buf);
               }
            }
         }
         else
            flag = FALSE;
      }
   }
}

void
parse_commands(char *cmdline)
{
   if (cmdline) {
      boolean flag = FALSE;
      int count = 0;

      struct dict_cmd *cmd;

      if (strlen(cmdline) == 1) 
         switch (*cmdline) {
            case '?':
               memset(cmdline, '\0', MAX_CMDLINE);
               strlcpy(cmdline, "help", 5);
               break;

            case 'S':
               warning("do NOT use this abbreviated command"
                     ", this is part of unofficial extension");
               memset(cmdline, '\0', MAX_CMDLINE);
               strlcpy(cmdline, "status", 7);
               break;

            case 'H':
               warning("do NOT use this abbreviated command"
                     ", this is part of unofficial extension");
               memset(cmdline, '\0', MAX_CMDLINE);
               strlcpy(cmdline, "help", 5);
               break;

            case 'Q':
               warning("do NOT use this abbreviated command"
                     ", this is part of unofficial extension");
               memset(cmdline, '\0', MAX_CMDLINE);
               strlcpy(cmdline, "quit", 5);
               break;

            default:
               break;
         }

      if (strcasecmp(cmdline, "history") == 0)
         print_history();
      else {
         if (*cmdline == '!') {
            unsigned int ix = 1;
            int entry = 0;
            char buffer[BUFSIZ];
            memset(buffer, '\0', BUFSIZ);
            for (ix = 1; ix < strlen(cmdline); ix++)
               buffer[ix - 1] = cmdline[ix];

            entry = (int)strtol(buffer, (char **)NULL, 10);
            if (entry) {
               history(hist, &hev, H_SET, entry);
               history(hist, &hev, H_CURR, entry);
               memset(buffer, '\0', BUFSIZ);
               strlcpy(buffer, hev.str, strlen(hev.str));
               if (strcasecmp(buffer, "history") == 0) {
                  print_history();
                  flag = TRUE;
               }
               else
                  cmdline = buffer;
            }
         }

         for (cmd = dcmd_tab; cmd->cmd_name; cmd++) {
            if (strcasecmp(cmdline, "exit") == 0) {
               memset(cmdline, '\0', MAX_CMDLINE);
               strlcpy(cmdline, "quit", 5);
            }

            if (strncasecmp(
                     cmd->cmd_name, cmdline, strlen(cmd->cmd_name)) == 0) {
               flag = TRUE;
               size_t stlen = strlen(cmdline) + 1;
               char *cmd_string = (char *)malloc(sizeof(char) * stlen);
               if (!cmd_string)
                  error("%s malloc()", __func__);

               strlcpy(cmd_string, cmdline, stlen);
               char *temp = cmd_string;

               char *string;
               for ((string = strsep(&cmd_string, SEP)), count = 0;
                     (count < MAXTOKEN) && string && (*string != '\0');
                     (string = strsep(&cmd_string, SEP)), count++)
                  cmd_tokens[count] = string;

               cmd_tokens[count] = 0;

               cmd_string = temp;
               if (cmd_string)
                  free(cmd_string);

               cmd->cmd_handler(count, cmdline);
            }
         }
      
         if (!flag)
            warning("warning unknown command '%s' entered", cmdline);
      }
   }
}

void
cleanup()
{
   if (dclient) {
      close(dclient->sockfd);
      free(dclient);
   }

   if (edit) {
      el_reset(edit);
      el_end(edit);
   }

   if (hist)
      history_end(hist);
}

void
print_history()
{
   int count = 0;
   history(hist, &hev, H_GETSIZE, count);

   int max = hev.num;
   if (max) {
      for (count = 1; count <= max; count++) {
         history(hist, &hev, H_SET, count);
         history(hist, &hev, H_CURR, count);
         printf("%d\t%s", count, hev.str);
      }
   }
   else 
      printf("no history yet!\n");
}
